﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor;

namespace FloatingWindowTools.FloatingMapbasicWindow
{
    public partial class MapbasicWindowUI : Form
    {
        private MapinfoWrapper mapinfo;

        public MapbasicWindowUI(MapbasicWindowWrapper mapbasicWindow, MapinfoWrapper mapInfo)
        {
            InitializeComponent();
            this.mapinfo = mapInfo;

            this.syntaxcontrol.SetHighlighting("Mapbasic");
            this.syntaxcontrol.ActiveTextAreaControl.TextArea.KeyUp += TextArea_KeyUp;

            this.MapbasicWindow = mapbasicWindow;
            this.Load += new EventHandler(MapbasicWindow_Load);
            this.FormClosed += new FormClosedEventHandler(MapbasicWindow_FormClosed);
            this.Resize += new EventHandler(MapbasicWindow_Resize);
            this.MapbasicWindow.TextUpdated += new Action<string>(MapbasicWindow_TextUpdated);
        }

        public MapbasicWindowWrapper MapbasicWindow { get; set; }

        void MapbasicWindow_TextUpdated(string obj)
        {
            if (!String.IsNullOrEmpty(obj))
            {
                this.syntaxcontrol.Invoke(new Action(() =>
                {
                    syntaxcontrol.Document.TextContent += obj;
                }));
            }
        }

        void TextArea_KeyUp(object sender, KeyEventArgs e)
        {
            switch (e.KeyData)
            {
                case Keys.Back:
                    this.DoEditAction(this.syntaxcontrol, new ICSharpCode.TextEditor.Actions.Backspace());
                    break;
                case Keys.Delete:
                    this.DoEditAction(this.syntaxcontrol, new ICSharpCode.TextEditor.Actions.Delete());
                    break;
                case Keys.Down:
                    this.DoEditAction(this.syntaxcontrol, new ICSharpCode.TextEditor.Actions.CaretDown());
                    break;
                case Keys.Left:
                    this.DoEditAction(this.syntaxcontrol, new ICSharpCode.TextEditor.Actions.CaretLeft());
                    break;
                case Keys.Return:
                    this.DoEditAction(this.syntaxcontrol, new ICSharpCode.TextEditor.Actions.Return());
                    break;
                case Keys.Right:
                    this.DoEditAction(this.syntaxcontrol, new ICSharpCode.TextEditor.Actions.CaretRight());
                    break;
                case Keys.Up:
                    this.DoEditAction(this.syntaxcontrol, new ICSharpCode.TextEditor.Actions.CaretUp());
                    break;
                default:
                    break;
            }
        }

        void MapbasicWindow_FormClosed(object sender, FormClosedEventArgs e)
        {
            this.MapbasicWindow.Close();
            this.MapbasicWindow.TextUpdated -= this.MapbasicWindow_TextUpdated;
        }

        /// <summary>Performs an action encapsulated in IEditAction.</summary>
        /// <remarks>
        /// There is an implementation of IEditAction for every action that 
        /// the user can invoke using a shortcut key (arrow keys, Ctrl+X, etc.)
        /// The editor control doesn't provide a public funciton to perform one
        /// of these actions directly, so I wrote DoEditAction() based on the
        /// code in TextArea.ExecuteDialogKey(). You can call ExecuteDialogKey
        /// directly, but it is more fragile because it takes a Keys value (e.g.
        /// Keys.Left) instead of the action to perform.
        /// <para/>
        /// Clipboard commands could also be done by calling methods in
        /// editor.ActiveTextAreaControl.TextArea.ClipboardHandler.
        /// </remarks>
        private void DoEditAction(TextEditorControl editor, ICSharpCode.TextEditor.Actions.IEditAction action)
        {
            if (editor != null && action != null)
            {
                var area = this.syntaxcontrol.ActiveTextAreaControl.TextArea;
                this.syntaxcontrol.BeginUpdate();
                try
                {
                    lock (editor.Document)
                    {
                        action.Execute(area);
                        if (area.SelectionManager.HasSomethingSelected && area.AutoClearSelection /*&& caretchanged*/)
                        {
                            if (area.Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal)
                            {
                                area.SelectionManager.ClearSelection();
                            }
                        }
                    }
                }
                finally
                {
                    this.syntaxcontrol.EndUpdate();
                    area.Caret.UpdateCaretPosition();
                }
            }
        }

        void MapbasicWindow_Load(object sender, EventArgs e)
        {
            Window mapinfoWindow = new Window(this.mapinfo, (int)Window.WindowType.WIN_MAPINFO);

            IntPtr thread = new IntPtr(AppDomain.GetCurrentThreadId());

            // Attach our .net thread to the thread of the window we reparented.
            // This fixs issue (http://code.google.com/p/nathansmapinfoprojects/issues/detail?id=1) which was causing MapInfo's dialogs to draw incorrectly.
            WinAPI.AttachThreadInput(thread, this.MapbasicWindow.OwnerThreadId, 1);

            WinAPI.SetParent(this.Handle, mapinfoWindow.Handle);

            //Set the parent of Mpabsic window to a picture box on the form.
            this.MapbasicWindow.Parent = this.pictureBox1.Handle;
            this.MapbasicWindow.RemoveCaptionBar();
            this.MapbasicWindow.RemoveBorder();
            this.MapbasicWindow.Maximize();

            //Add one to the width to update the control.
            this.Width += 1;

            this.mapinfo.Do("Alter Button ID 2005 Disable");
            //MapInfo.MiPro.Interop.InteropServices.MapInfoApplication.Do("Alter Menu Item Load Disable");
        }

        private void MapbasicWindow_Resize(object sender, EventArgs e)
        {
            if (this.MapbasicWindow != null)
            {
                this.MapbasicWindow.UpdateSize(this.pictureBox1.Width, this.pictureBox1.Height);
            }
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams createParams = base.CreateParams;

                // Appear as a pop-up window
                int style = createParams.Style;
                createParams.Style = style + unchecked((int)WinAPI.WindowStyles.WS_POPUP);

                return createParams;
            }
        }
    }
}
